package pl.edu.agh.ki.neuralnetwork.layer;

import java.util.List;

import pl.edu.agh.ki.neuralnetwork.exceptions.NeuronNotConnectedException;
import pl.edu.agh.ki.neuralnetwork.neurons.InnerNeuron;
import pl.edu.agh.ki.neuralnetwork.neurons.SigmoidalNeuron;

public class GrossbergLayer extends SimpleLayer<InnerNeuron> {
	
	private boolean sigmoid;
	private List<Double> learningSpeedList;
	
	public void setLearningSpeedList(List<Double> learningSpeedList) {
		this.learningSpeedList = learningSpeedList;
	}

	public GrossbergLayer(boolean sigmoid) {
		this.sigmoid = sigmoid;
	}
	
	/**
	 * function returns learning coefficient (it should be decreasing)
	 * 
	 * @param iteration
	 * @return
	 */
//	private double n(int iteration) {
//		if (iteration < 1000)
//			return 0.2;  // 0.2
//		else if (iteration < 2000)
//			return 0.1; // 0.1
//		else if (iteration < 3000)
//			return 0.05; // 0.05
//		else if (iteration < 4000)
//			return 0.025; // 0.025
//		else
//			return 0.1; // 0.0125
//	}
	/**
	 * function returns learning coefficient (it should be decreasing)
	 * 
	 * @param iteration
	 * @return
	 */
	private double n(int iteration) {
		int i=iteration/1000;
		if(i>learningSpeedList.size()-1)
			i = learningSpeedList.size()-1;
		return learningSpeedList.get(i);
	}

	public void learn(int iteration, InnerNeuron winner, List<Double> outputs) {
		try {
			if(sigmoid) {
				for (int i=0; i<list.size(); i++) {
					InnerNeuron neuron = list.get(i); 
					double outputWanted = outputs.get(i);
					double oldWeight = neuron.getWeight(winner);
					double outputActual = SigmoidalNeuron.f(oldWeight);
					//double newWeight = oldWeight + n(iteration)*(output - oldWeight);
					double newWeight = oldWeight+n(iteration)*(outputWanted-outputActual)*outputActual*(1.0-outputActual);
//						System.out.println("old: "+oldWeight+"\noutput: "+outputActual+"\noutputWanted: "+outputWanted+"\nnew: "+newWeight+"\n");
					neuron.setWeight(winner, newWeight);
				}
			} else {
				for (int i=0; i<list.size(); i++) {
					InnerNeuron neuron = list.get(i); 
					double output = outputs.get(i);
					double oldWeight = neuron.getWeight(winner);
					double newWeight = oldWeight + n(iteration)*(output - oldWeight);
		//				System.out.println("old: "+oldWeight+"\noutput: "+output+"\nnew: "+newWeight);
					neuron.setWeight(winner, newWeight);
				}
			}
		} catch (NeuronNotConnectedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public String toString() {
		StringBuilder sb = new StringBuilder();
		for(int i=0; i<list.size(); i++) {
			sb.append("Neuron "+i+": \n");
			for(double weight : list.get(i).getWeights()) {
				sb.append("\t"+weight+"\n");
			}
		}
		return sb.toString();
	}

}
